<template>
  <div class="pvtUi">
    <slot name="colGroup" />
    <div class="pvtUi-header">
      <VRendererCell
        :renderer-items="rendererItems"
        :renderer-name="state.rendererName"
        :localeStrings="localeStrings"
        @update:renderer-name="onUpdateRendererName"
      >
        <template v-if="$slots.rendererCell" #rendererCell>
          <slot name="rendererCell" />
        </template>
      </VRendererCell>

      <VDragAndDropCell
        classes="pvtAxisContainer pvtUnused pvtHorizList"
        cell-type="unused"
        :attributeNames="unusedAttrs"
        :all-filters="allFilters"
        :fields="fields"
        :value-filter="state.valueFilter"
        :restricted-from-drag-drop="state.restrictedFromDragDrop"
        :hide-filter-box-of-unused-attributes="
          state.hideFilterBoxOfUnusedAttributes
        "
        :z-indices="pivotUiState.zIndices"
        :max-z-index="pivotUiState.maxZIndex"
        :open-status="pivotUiState.openStatus"
        @update:z-index-of-filter-box="onMoveFilterBoxToTop"
        @update:unselected-filter-values="onUpdateValueFilter"
        @update:open-status-of-filter-box="onUpdateOpenStatus"
        @update:dragged-attribute="handleDraggedAttribute"
      />
    </div>
    <div class="pvtUi-toolbar">
      <VAggregatorCell
        :aggregator-items="aggregatorItems"
        :aggregator-name="state.aggregatorName"
        :row-order="state.rowOrder"
        :col-order="state.colOrder"
        :attributeNames="attributeNames"
        :hidden-from-aggregators="state.hiddenFromAggregators"
        :vals="multiple ? actualVals : state.vals"
        :multiple="multiple"
        :fields="fields"
        :localeStrings="localeStrings"
        @update:aggregator-name="onUpdateAggregatorName"
        @update:row-order="onUpdateRowOrder"
        @update:col-order="onUpdateColOrder"
        @update:vals="handleUpdateVals"
      >
        <template v-if="$slots.aggregatorCell" #aggregatorCell>
          <slot name="aggregatorCell" />
        </template>
      </VAggregatorCell>

      <VDragAndDropCell
        classes="pvtAxisContainer pvtHorizList pvtCols"
        cell-type="cols"
        :multiple="multiple"
        :fields="fields"
        :attributeNames="colAttrs"
        :all-filters="allFilters"
        :value-filter="state.valueFilter"
        :restricted-from-drag-drop="state.restrictedFromDragDrop"
        :hide-filter-box-of-unused-attributes="
          state.hideFilterBoxOfUnusedAttributes
        "
        :z-indices="pivotUiState.zIndices"
        :max-z-index="pivotUiState.maxZIndex"
        :open-status="pivotUiState.openStatus"
        @update:z-index-of-filter-box="onMoveFilterBoxToTop"
        @update:unselected-filter-values="onUpdateValueFilter"
        @update:open-status-of-filter-box="onUpdateOpenStatus"
        @update:dragged-attribute="handleDraggedAttribute"
      >
        <template v-if="$slots.pvtAttr" #pvtAttr="propsValue">
          <slot name="pvtAttr" v-bind="propsValue" />
        </template>
      </VDragAndDropCell>
    </div>
    <div class="pvtUi-content">
      <VDragAndDropCell
        classes="pvtAxisContainer pvtVertList pvtRows"
        cell-type="rows"
        :fields="fields"
        :attributeNames="rowAttrs"
        :all-filters="allFilters"
        :value-filter="state.valueFilter"
        :restricted-from-drag-drop="state.restrictedFromDragDrop"
        :hide-filter-box-of-unused-attributes="
          state.hideFilterBoxOfUnusedAttributes
        "
        :z-indices="pivotUiState.zIndices"
        :max-z-index="pivotUiState.maxZIndex"
        :open-status="pivotUiState.openStatus"
        @update:z-index-of-filter-box="onMoveFilterBoxToTop"
        @update:unselected-filter-values="onUpdateValueFilter"
        @update:open-status-of-filter-box="onUpdateOpenStatus"
        @update:dragged-attribute="handleDraggedAttribute"
      >
        <template #pvtAttr="propsValue">
          <slot v-bind="propsValue" name="pvtAttr" />
        </template>
      </VDragAndDropCell>
      <div class="pvtOutput">
        <template v-if="$slots.outputSlot">
          <slot name="outputSlot" :output-slot="{ pivotData }" />
        </template>
        <template v-else-if="$slots.output">
          <slot name="output" />
        </template>
        <template v-else>
          <VPivottable v-bind="pivotProps" :fields="fields" :groupMode="groupMode" />
        </template>
      </div>
    </div>
  </div>
</template>

<script setup>
import { defaultProps, PivotData, sortAs } from '@/pivottable/helper';
import VRendererCell from './VRendererCell.vue';
import VAggregatorCell from './VAggregatorCell.vue';
import VDragAndDropCell from './VDragAndDropCell.vue';
import VPivottable from '../pivottable/VPivottable.vue';
import TableRenderer from '../pivottable/renderer/index';
import { computed, watch } from 'vue';
import {
  usePropsState,
  useMaterializeInput,
  usePivotUiState,
  provideFilterBox,
} from '@/pivottable/composables';

const emit = defineEmits(['update:vals', 'change']);

const props = defineProps({
  ...defaultProps,
  hiddenAttributes: {
    type: Array,
    default: () => [],
  },
  hiddenFromAggregators: {
    type: Array,
    default: () => [],
  },
  hiddenFromDragDrop: {
    type: Array,
    default: () => [],
  },
  restrictedFromDragDrop: {
    type: Array,
    default: () => [],
  },
  menuLimit: {
    type: Number,
    default: 500,
  },
  pivotModel: {
    type: Object,
    default: () => ({}),
  },
  hideFilterBoxOfUnusedAttributes: {
    type: Boolean,
    default: false,
  },
  actualVals: {
    type: Array,
    default: () => [],
  },
});
const {
  state,
  localeStrings,
  updateMultiple,
  onUpdateValueFilter,
  onUpdateRendererName,
  onUpdateAggregatorName,
  onDraggedAttribute,
  onUpdateRowOrder,
  onUpdateColOrder,
  onUpdateVals,
} = usePropsState(props);

const {
  state: pivotUiState,
  onMoveFilterBoxToTop,
  onUpdateOpenStatus,
  onUpdateUnusedOrder,
} = usePivotUiState();

const { allFilters, materializedInput, processData } = useMaterializeInput(
  props.data,
  {
    derivedAttributes: computed(() => props.derivedAttributes),
  },
);

const rendererItems = computed(() =>
  Object.keys(state.renderers).length ? state.renderers : TableRenderer,
);
const aggregatorItems = computed(() => state.aggregators);
const rowAttrs = computed(() => {
  return state.rows.filter(
    e =>
      !state.hiddenAttributes.includes(e) &&
      !state.hiddenFromDragDrop.includes(e),
  );
});
const colAttrs = computed(() => {
  return state.cols.filter(
    e =>
      !state.hiddenAttributes.includes(e) &&
      !state.hiddenFromDragDrop.includes(e),
  );
});
const attributeNames = computed(() => {
  return Object.keys(allFilters.value).filter(
    e =>
      !state.hiddenAttributes.includes(e) &&
      !state.hiddenFromAggregators.includes(e),
  );
});
const unusedAttrs = computed(() => {
  return attributeNames.value
    .filter(
      e =>
        !state.rows.includes(e) &&
        !state.cols.includes(e) &&
        !state.hiddenAttributes.includes(e) &&
        !state.hiddenFromDragDrop.includes(e),
    )
    .sort(sortAs(state.unusedOrder));
});

const pivotData = computed(() => new PivotData(state));
const pivotProps = computed(() => ({
  data: state.data,
  aggregators: state.aggregators,
  aggregatorName: state.aggregatorName,
  heatmapMode: state.heatmapMode,
  tableOptions: state.tableOptions,
  renderers: rendererItems.value,
  rendererName: state.rendererName,
  locale: state.locale,
  languagePack: state.languagePack,
  showRowTotal: state.showRowTotal,
  showColTotal: state.showColTotal,
  cols: state.cols,
  rows: state.rows,
  vals: state.vals,
  attributes: state.attributes,
  valueFilter: state.valueFilter,
  sorters: state.sorters,
  derivedAttributes: state.derivedAttributes,
  rowOrder: state.rowOrder,
  colOrder: state.colOrder,
  tableMaxWidth: state.tableMaxWidth,
  localeStrings: localeStrings.value,
}));

onUpdateUnusedOrder(props.unusedAttrs);

provideFilterBox(props);

watch(
  [allFilters, materializedInput],
  () => {
    updateMultiple({
      ...state,
      allFilters: allFilters.value,
      materializedInput: materializedInput.value,
    });
  },
  {
    deep: true,
  },
);

watch(
  [() => props.data, () => props.cols, () => props.vals, () => props.rows],
  () => {
    updateMultiple({
      ...state,
      data: props.data,
      cols: props.cols,
      vals: props.vals,
      rows: props.rows,
    });
    processData(props.data);
  },
  {
    immediate: true,
    deep: true,
  },
);

const emitData = () => {
  if (props.multiple) {
    const cols = [...state.cols];
    if (cols.includes('srfname')) {
      cols.pop();
    }
    emit('change', {
      cols: cols,
      vals: state.actualVals,
      rows: state.rows,
      aggregatorName: state.aggregatorName,
      rendererName: state.rendererName,
    });
  } else {
    emit('change', {
      cols: state.cols,
      vals: state.vals,
      rows: state.rows,
      aggregatorName: state.aggregatorName,
      rendererName: state.rendererName,
    });
  }
}

const handleUpdateVals = vals => {
  if (props.multiple) {
    state.actualVals = vals;
    emit('update:vals', vals);
  } else {
    onUpdateVals(vals);
  }
  emitData();
};

const handleDraggedAttribute = ({ key, value }) => {
  onDraggedAttribute({ key, value });
  emitData();
};
</script>
